package com.atguigu.spzx.user.service.impl;

import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.RandomUtil;
import com.atguigu.spzx.common.consts.SpzxConsts;
import com.atguigu.spzx.common.util.AuthUtil;
import com.atguigu.spzx.common.util.IpUtil;
import com.atguigu.spzx.common.util.RegexValidateUtils;
import com.atguigu.spzx.model.dto.api.ApiUserRegisterDTO;
import com.atguigu.spzx.model.entity.user.UserInfo;
import com.atguigu.spzx.model.exception.SpzxException;
import com.atguigu.spzx.model.vo.api.user.ApiUserInfoVO;
import com.atguigu.spzx.model.vo.common.ResultCodeEnum;
import com.atguigu.spzx.user.mapper.UserInfoMapper;
import com.atguigu.spzx.user.service.UserInfoService;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.DigestUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import java.util.Date;
import java.util.concurrent.TimeUnit;

@Service
public class UserInfoServiceImpl extends ServiceImpl<UserInfoMapper , UserInfo> implements UserInfoService {
    @Resource
    StringRedisTemplate stringRedisTemplate;
    @Lazy //懒注入：使用时再注入
    @Resource
    UserInfoService userInfoService;
    @Resource
    RedisTemplate redisTemplate;
    @Override
    public void register(ApiUserRegisterDTO userRegisterDTO) {
        //1、校验参数
        String username = userRegisterDTO.getUsername();
        String password = userRegisterDTO.getPassword();
        String nickName = userRegisterDTO.getNickName();
        String code = userRegisterDTO.getCode();
        if(StringUtils.isEmpty(username)||
                !RegexValidateUtils.checkCellphone(username)||
                StringUtils.isEmpty(password)||
                StringUtils.isEmpty(nickName)||
                StringUtils.isEmpty(code)
        ){
            throw new SpzxException(ResultCodeEnum.PARAMS_ERROR);
        }
        //2、校验验证码
        //获取redis中缓存的验证码比较: 必须使用存入验证码的redis客户端模板类对象来获取
        String cacheCode = stringRedisTemplate.opsForValue().get(SpzxConsts.SPZX_CODE_PREFIX + username);
        if(!code.equals(cacheCode)){ // == equals
            throw new SpzxException(ResultCodeEnum.CODE_ERROR);
        }
        //3、用户是否已注册
        long count = this.count(Wrappers.lambdaQuery(UserInfo.class).eq(UserInfo::getUsername, username));
        if(count>0){
            throw new SpzxException(ResultCodeEnum.MOBILE_EXISTS_ERROR);
        }
        //4、密码加密
        //生成盐
        String salt = RandomUtil.randomString(6);
        String encodePwd = DigestUtils.md5DigestAsHex((DigestUtils.md5DigestAsHex(password.getBytes()) + salt).getBytes());
        //5、保存数据
        //组装数据：
        UserInfo userInfo = new UserInfo();
        userInfo.setUsername(username);
        userInfo.setPassword(encodePwd);//一定要使用加密后的密码
        userInfo.setNickName(nickName);
        userInfo.setAvatar("http://192.168.1.170:8848/nacos/img/logo-2000-390.svg");
        userInfo.setSex(1);
        userInfo.setPhone(username);
//        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
//        String ipAddress = IpUtil.getIpAddress(requestAttributes.getRequest());//获取浏览器的ip地址
//        userInfo.setLastLoginIp(ipAddress);
//        userInfo.setLastLoginTime(new Date());
        userInfo.setStatus(1);
        userInfo.setSalt(salt);

        baseMapper.insert(userInfo);
    }

    @Override
    public String login(ApiUserRegisterDTO userRegisterDTO) {
        //1、校验参数
        String username = userRegisterDTO.getUsername();
        String password = userRegisterDTO.getPassword();
        if(StringUtils.isEmpty(username)||
                !RegexValidateUtils.checkCellphone(username)||
                StringUtils.isEmpty(password)
        ){
            throw new SpzxException(ResultCodeEnum.PARAMS_ERROR);
        }
        //2、根据账号查询数据库用户信息
        UserInfo userInfo = baseMapper.selectOne(Wrappers.lambdaQuery(UserInfo.class)
                .eq(UserInfo::getUsername, username));
        if(ObjectUtils.isEmpty(userInfo)){
            throw new SpzxException(ResultCodeEnum.LOGIN_USERNAME_ERROR);
        }
        //3、密码加盐加密 和数据库密文比较：
        String dbPwd = userInfo.getPassword();
        //明文密码加密：
        String encodePwd = DigestUtils.md5DigestAsHex((DigestUtils.md5DigestAsHex(password.getBytes()) +
                userInfo.getSalt()).getBytes());
        if(!dbPwd.equals(encodePwd)){
            throw new SpzxException(ResultCodeEnum.LOGIN_PASSWORD_ERROR);
        }
        //4、更新最后一次登录的时间和ip地址
        ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        String ipAddress = IpUtil.getIpAddress(requestAttributes.getRequest());//获取浏览器的ip地址
        userInfo.setLastLoginIp(ipAddress);
        userInfo.setLastLoginTime(new Date());

        //5、保持登录状态： 保存到redis中
        String token = IdUtil.getSnowflake(1, 1).nextIdStr();
        //数据脱敏
        userInfo.setPassword(null);
        userInfo.setOpenId(null);
        // 更新数据时，只会更新不为空的字段值
//        this.updateById(userInfo);
        //Aync 基于aop实现的，如果在当前业务类的方法中通过this调用其他方法，this没有被代理
        //代理对象调用方法时，先执行代理对象的方法，在他的方法中 通过invoke方式去调用目标对象的真实方法

        userInfoService.updateLoginInfo(userInfo);

        redisTemplate.opsForValue().set(SpzxConsts.SPZX_USER_LOGIN_PREFIX+token , userInfo , 3 , TimeUnit.DAYS);
        System.out.println("login: " + new Date());
        //6、返回token
        return token;
    }

    @Override
    public ApiUserInfoVO getCurrentUserInfo() {
        ApiUserInfoVO userInfoVO = new ApiUserInfoVO();
        userInfoVO.setAvatar(AuthUtil.getUserInfo().getAvatar());
        userInfoVO.setNickName(AuthUtil.getUserInfo().getNickName());
        return userInfoVO;
    }

    @Override
    public void logout(String token) {
        redisTemplate.delete(SpzxConsts.SPZX_USER_LOGIN_PREFIX+token);
    }
    //更新用户登录时间 ip的业务方法
    @Async //异步执行： 异步执行最好配置最大线程数 和 任务队列的长度
    @Override
    public void updateLoginInfo(UserInfo userInfo) {
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        this.update(Wrappers.lambdaUpdate(UserInfo.class)
                .set(UserInfo::getLastLoginIp , userInfo.getLastLoginIp())
                .set(UserInfo::getLastLoginTime , userInfo.getLastLoginTime())
                .set(UserInfo::getUpdateTime , new Date())
                .eq(UserInfo::getId , userInfo.getId()));
        System.out.println("updateLoginInfo: " + new Date());
    }


}
